package assembler;

import java.io.*;
import java.util.Hashtable;
import java.util.Vector;

class Assembler
{

    Assembler()
    {
    }

    private static int passOne(Hashtable hashtable, AsmFileReader asmfilereader)
    {
        int i = 0;
        int j = 0;
        Vector vector1 = new Vector();
        for(Vector vector = asmfilereader.nextLine(); vector != null; vector = asmfilereader.nextLine())
        {
            ParsedLine parsedline = new ParsedLine(vector);
            if(parsedline.getDirective() != null)
            {
                if(parsedline.getDirective().equals(".break"))
                {
                    if(parsedline.getLabel() != null)
                    {
                        hashtable.put(parsedline.getLabel(), new Integer(i));
                    }
                    i += 4;
                } else
                {
                    vector1.add(parsedline);
                }
            } else
            {
                String s = parsedline.getLabel();
                if(s != null)
                {
                    if(hashtable.containsKey(s))
                    {
                        parsedline.setError();
                        System.out.println("Duplicate Label: The label " + s + " is defined at multiple " + "locations including line " + parsedline.getLineNum() + ".");
                    } else
                    {
                        hashtable.put(s, new Integer(i));
                    }
                }
                i += 4 * parsedline.getExpFactor();
            }
            if(parsedline.getError())
            {
                j++;
            }
        }

        for(int k = 0; k < vector1.size(); k++)
        {
            ParsedLine parsedline1 = (ParsedLine)vector1.elementAt(k);
            String s1 = parsedline1.getLabel();
            String s2 = parsedline1.getDirective();
            if(s1 != null)
            {
                if(hashtable.containsKey(s1))
                {
                    Instructions.printError(parsedline1, "The label " + s1 + " is defined at multiple " + "locations including this line.");
                    j++;
                } else
                {
                    hashtable.put(s1, new Integer(i));
                }
            }
            int l = parsedline1.getDataCount();
            if(l == 0)
            {
                l = 1;
            }
            if(s2.equals(".word"))
            {
                i += 4 * l;
            } else
            if(s2.equals(".half"))
            {
                i = i + 2 * l + (2 * l) % 4;
            } else
            if(s2.equals(".byte"))
            {
                i += l;
                if(l % 4 != 0)
                {
                    i += 4 - l % 4;
                }
            } else
            if(s2.equals(".space"))
            {
                int i1 = parsedline1.getData(0);
                i += i1;
                if(i1 % 4 != 0)
                {
                    i += 4 - i1 % 4;
                }
            } else
            if(!s2.equals(".break"))
            {
                Instructions.printError(parsedline1, "Unknown assembler directive " + s2 + ".");
                j++;
            }
        }

        return j;
    }

    private static int passTwo(Hashtable hashtable, AsmFileReader asmfilereader, PrintWriter printwriter)
    {
        int i = 0;
        Vector vector1 = new Vector();
        int j = 0;
        for(Vector vector = asmfilereader.nextLine(); vector != null; vector = asmfilereader.nextLine())
        {
            ParsedLine parsedline = new ParsedLine(vector);
            if(parsedline.getDirective() != null)
            {
                if(parsedline.getDirective().equals(".break"))
                {
                    printwriter.println("* break");
                    printwriter.println("** break");
                    printwriter.println("00001111000011110000111100001111");
                    j += 4;
                } else
                {
                    vector1.add(parsedline);
                }
            } else
            {
                j = Instructions.generate(parsedline, printwriter, hashtable, j);
            }
            if(parsedline.getError())
            {
                i++;
            }
        }

        for(int k = 0; k < vector1.size(); k++)
        {
            ParsedLine parsedline1 = (ParsedLine)vector1.elementAt(k);
            String s = parsedline1.getLabel();
            String s1 = parsedline1.getDirective();
            if(s != null)
            {
                printwriter.println("* " + s + " " + s1);
            }
            int l = parsedline1.getDataCount();
            if(s1.equals(".word"))
            {
                if(l == 0)
                {
                    printwriter.println(binString(0, 32));
                } else
                {
                    for(int i1 = 1; i1 <= l; i1++)
                    {
                        printwriter.println(binString(parsedline1.getData(i1 - 1), 32));
                    }

                }
            } else
            if(s1.equals(".half"))
            {
                if(l == 0)
                {
                    printwriter.println(binString(0, 32));
                } else
                {
                    for(int j1 = 1; j1 <= l; j1++)
                    {
                        printwriter.print(binString(parsedline1.getData(j1 - 1), 16));
                        if(j1 % 2 == 0)
                        {
                            printwriter.println();
                        }
                    }

                    for(int i2 = 0; i2 < ((2 * l) % 4) / 2; i2++)
                    {
                        printwriter.println(binString(0, 16));
                    }

                }
            } else
            if(s1.equals(".byte"))
            {
                if(l == 0)
                {
                    printwriter.println(binString(0, 32));
                } else
                {
                    for(int k1 = 1; k1 <= l; k1++)
                    {
                        printwriter.print(binString(parsedline1.getData(k1 - 1), 8));
                        if(k1 % 4 == 0)
                        {
                            printwriter.println();
                        }
                    }

                    if(l % 4 != 0)
                    {
                        for(int j2 = 0; j2 < 4 - l % 4; j2++)
                        {
                            printwriter.print(binString(0, 8));
                        }

                        printwriter.println();
                    }
                }
            } else
            if(s1.equals(".space"))
            {
                for(int l1 = 1; l1 <= (parsedline1.getData(0) + 3) / 4; l1++)
                {
                    printwriter.println(binString(0, 32));
                }

            } else
            {
                Instructions.printError(parsedline1, "Unknown assembler directive " + s1 + ".");
                i++;
                System.out.println("Crap! - This shouldn't happen!");
            }
        }

        return i;
    }

    public static String binString(int i, int j)
    {
        String s = Integer.toBinaryString(i);
        if(s.length() > j)
        {
            s = s.substring(s.length() - j, s.length());
        } else
        {
            for(int k = s.length(); k < j; k++)
            {
                s = "0" + s;
            }

        }
        return s;
    }

    public static void main(String args[])
    {
        if(args.length != 2)
        {
            System.out.println("Usage: java Assembler <source> <executable>");
            System.out.println();
            System.out.println("          <source> : Name of the source file to assemble.");
            System.out.println("       <executable>: Name of the executable file to produce.");
        } else
        {
            System.out.println("Assembling " + args[0] + " into " + args[1] + ".");
            System.out.println("Pass 1: Building symbol table...");
            AsmFileReader asmfilereader = new AsmFileReader(args[0]);
            Hashtable hashtable = new Hashtable();
            hashtable.put("STDIN", new Integer(-4));
            hashtable.put("STDOUT", new Integer(-8));
            int i = passOne(hashtable, asmfilereader);
            if(i != 0)
            {
                System.out.println(i + " lines with errors.");
                System.out.println("Executable not produced.");
            } else
            {
                try
                {
                    PrintWriter printwriter = new PrintWriter(new FileWriter(args[1]));
                    AsmFileReader asmfilereader1 = new AsmFileReader(args[0]);
                    System.out.println("Pass 2: Translating code to machine language...");
                    int j = passTwo(hashtable, asmfilereader1, printwriter);
                    printwriter.close();
                    if(j != 0)
                    {
                        System.out.println(j + " lines with errors.");
                        System.out.println("Executable not produced.");
                        (new File(args[1])).delete();
                    } else
                    {
                        System.out.println("Assembly complete.");
                    }
                }
                catch(IOException ioexception)
                {
                    System.out.println("Unable to create executable file: " + args[1] + ".");
                }
            }
        }
    }
}
